package com.interest.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.alibaba.fastjson.serializer.SerializerFeature;

/**
 * Json 工具类 wangguangyu qrw:使用FastJson重新写了代码，性能有大幅度提升，并且减少了内存和CPU消耗
 */
public class JsonUtil {
	private static ConcurrentHashMap<String, String> pathCache = new ConcurrentHashMap<String, String>(1024, 0.75f, 1);

	private static int CACHE_SIZE = 10240;

	static {
		JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
	}

	public static String getStringValue(JSONObject jsonData, String path) {
		Object val = getValue(jsonData, path, true);
		return val != null ? val.toString() : null;
	}

	public static int getIntValue(JSONObject jsonData, String path) {
		Object val = getValue(jsonData, path, false);
		return val != null ? (int) val : null;
	}

	public static boolean getBooleanValue(JSONObject jsonData, String path) {
		Object val = getValue(jsonData, path, false);
		return val != null ? (boolean) val : null;
	}

	public static Object getValue(JSONObject jsonData, String path, boolean jsonPath_flag) {
		try {
			if (jsonPath_flag) {
				return JSONPath.eval(jsonData, path);
			} else {
				String path_new = transferToJsonPath(path);
				return JSONPath.eval(jsonData, path_new);
			}
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 
	 * @param json
	 * @param path
	 * @return
	 */
	public static List getSubListFromJSON(JSONObject jsonData, String path) {
		List value = (List) getValue(jsonData, path, false);
		return value != null ? value : new ArrayList();
	}

	// 转化成符合jsonpath格式的路径：id_policy.insurants.0.beneList.0.person.name -->
	// id_policy.insurants[0].beneList[0].person.name
	public static String transferToJsonPath(String path) {
		String path_inner = pathCache.get(path);

		if (path_inner == null) {
			path_inner = path.replaceAll("\\.([0-9]+)([\\.]?)", "[$1\\]$2");
			pathCache.putIfAbsent(path, path_inner);
		}

		return path_inner;
	}

	// will use the fastjson function to modify the json content
	// 使用FastJson的set方法来设置值,如果成功则返回true，否则 false
	public static boolean setValueByJsonPath(JSONObject rootObject, String path, Object value) {
		try {
			JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
			JSONPath jsonpath = JSONPath.compile(path);
			return jsonpath.set(rootObject, value);
		} catch (Exception e) {
			return false;
		}
	}

	// 使用Jsonpath格式设置值 path= a.b[0].c.d[0].e
	public static boolean setValue(JSONObject rootObject, String path, Object value) {
		return setValue(rootObject, path, value, true);
	}

	// 设置Json里面的内容，如果Json里面没有这个值，则生成这个key
	public static boolean setValue(JSONObject rootObject, String path, Object value, boolean jsonPath_format) {
		String parentPath = jsonPath_format ? path : transferToJsonPath(path);

		String currentPath = "";
		String key = "";
		Object valueObj = value;

		boolean result = false;
		boolean lastArrayNode = false;

		while (parentPath != null && !(result = setValueByJsonPath(rootObject, parentPath, valueObj))) {

			int indexPos = parentPath.lastIndexOf(".");

			currentPath = parentPath.substring(indexPos + 1);

			// System.out.println(valueObj.toString());
			if (indexPos >= 0) {
				parentPath = parentPath.substring(0, indexPos);
			} else {
				int indexParentPathPos2 = parentPath.indexOf('[');
				if (indexParentPathPos2 > 0) {
					lastArrayNode = true;
				}
				parentPath = null;
			}

			int indexPos2 = currentPath.indexOf('[');
			if (indexPos2 > 0) {
				// System.out.println("array");
				key = currentPath.substring(0, indexPos2);

				List tempObj = null;
				if (parentPath != null) {
					// tempObj = (JSONArray) getValue(rootObject, parentPath +
					// "." + key, true);
					Object obj = getValue(rootObject, parentPath + "." + key, true);
					if (!"".equals(obj)) {
						tempObj = (List) obj;
					}
				} else {
					Object obj = getValue(rootObject, key, true);
					if (!"".equals(obj)) {
						tempObj = (List) obj;
					}
				}

				if (tempObj == null) {
					tempObj = new ArrayList();
				}

				tempObj.add(valueObj);
				valueObj = tempObj;
			} else {
				// System.out.println("String");
				key = currentPath;
			}

			if (lastArrayNode) {
				rootObject.put(key, valueObj);
			} else {
				JSONObject tempObj = null;
				Object obj = getValue(rootObject, parentPath, true);
				if (!"".equals(obj)) {
					tempObj = (JSONObject) obj;
				}

				if (tempObj == null) {
					tempObj = new JSONObject();
				}
				tempObj.put(key, valueObj);
				valueObj = tempObj;
			}
			// System.out.println(parentPath + "|" + currentPath);
			// System.out.println(valueObj.toString().replaceAll("=", ":"));
		}

		return result;
	}

	/**
	 * 
	 * @param json
	 * @param path
	 * @return
	 */
	public static Object getObjectFromJson(JSONObject json, String path) {
		return JsonUtil.getValue(json, path, false);
	}

	/**
	 * 支持多级添加
	 * 
	 * @param jsonData
	 * @param path
	 * @param jsonValue
	 * @wangguangyu
	 */
	public static void setStringValue(JSONObject jsonData, String path, String jsonValue) {
		setValue(jsonData, path, jsonValue, false);
	}

	public static boolean isNumeric(String str) {
		Pattern pattern = Pattern.compile("[0-9]*");
		Matcher isNum = pattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}

	/**
	 * Convert a JSONObject into a well-formed, element-normal XML string.
	 * 
	 * @param object
	 *            A JSONObject.
	 * @return A string.
	 * @throws JSONException
	 */
	public static String toXMLString(Object object) throws JSONException {
		return toXMLString(object, null);
	}

	public static String escape(String string) {
		StringBuilder sb = new StringBuilder(string.length());
		for (int i = 0, length = string.length(); i < length; i++) {
			char c = string.charAt(i);
			switch (c) {
			case '&':
				sb.append("&amp;");
				break;
			case '<':
				sb.append("&lt;");
				break;
			case '>':
				sb.append("&gt;");
				break;
			case '"':
				sb.append("&quot;");
				break;
			case '\'':
				sb.append("&apos;");
				break;
			default:
				sb.append(c);
			}
		}
		return sb.toString();
	}

	/**
	 * Convert a JSONObject into a well-formed, element-normal XML string.
	 * 
	 * @param object
	 *            A JSONObject.
	 * @param tagName
	 *            The optional name of the enclosing tag.
	 * @return A string.
	 * @throws JSONException
	 */
	public static String toXMLString(Object object, String tagName) throws JSONException {
		StringBuilder sb = new StringBuilder();
		int i;
		JSONArray ja;
		JSONObject jo;
		String key;
		Iterator<String> keys;
		int length;
		String string;
		Object value;
		if (object instanceof JSONObject) {

			// Emit <tagName>

			if (tagName != null) {
				sb.append('<');
				sb.append(tagName);
				sb.append('>');
			}

			// Loop thru the keys.

			jo = (JSONObject) object;
			keys = jo.keySet().iterator();
			while (keys.hasNext()) {
				key = keys.next();
				value = jo.get(key);
				if (value == null) {
					value = "";
				}
				string = value instanceof String ? (String) value : null;

				// Emit content in body

				if ("content".equals(key)) {
					if (value instanceof JSONArray) {
						ja = (JSONArray) value;
						length = ja.size();
						for (i = 0; i < length; i += 1) {
							if (i > 0) {
								sb.append('\n');
							}
							sb.append(escape(ja.get(i).toString()));
						}
					} else {
						sb.append(escape(value.toString()));
					}

					// Emit an array of similar keys

				} else if (value instanceof JSONArray) {
					ja = (JSONArray) value;
					length = ja.size();
					for (i = 0; i < length; i += 1) {
						value = ja.get(i);
						if (value instanceof JSONArray) {
							sb.append('<');
							sb.append(key);
							sb.append('>');
							sb.append(toXMLString(value));
							sb.append("</");
							sb.append(key);
							sb.append('>');
						} else {
							sb.append(toXMLString(value, key));
						}
					}
				} else if ("".equals(value)) {
					sb.append('<');
					sb.append(key);
					sb.append("/>");

					// Emit a new tag <k>
				} else {
					sb.append(toXMLString(value, key));
				}
			}
			if (tagName != null) {
				// Emit the </tagname> close tag
				sb.append("</");
				sb.append(tagName);
				sb.append('>');
			}
			return sb.toString();

			// XML does not have good support for arrays. If an array appears in
			// a place
			// where XML is lacking, synthesize an <array> element.

		} else {
			if (object instanceof JSONArray) {
				ja = (JSONArray) object;
				length = ja.size();
				for (i = 0; i < length; i += 1) {
					sb.append(toXMLString(ja.get(i), tagName == null ? "array" : tagName));
				}
				return sb.toString();
			} else {
				string = (object == null) ? "null" : escape(object.toString());
				return (tagName == null) ? "\"" + string + "\""
						: (string.length() == 0) ? "<" + tagName + "/>"
								: "<" + tagName + ">" + string + "</" + tagName + ">";
			}
		}
	}

}
